#ifndef MEMSTREAM_INC
#define MEMSTREAM_INC

#include "Stream.h"

class MemStream
{
public:
	MemStream() : _pos( 0 ), _len( 0 ), _writer( nullptr )
	{
	}
	MemStream( const void * buf, size_t len ) : _pos(0), _len(len), _writer(reinterpret_cast<const UInt8 *>(buf))
	{
	}

public:
	inline size_t size()	{ return _reader.size();}
	inline void reset()		{ _pos = 0; 			}
	inline size_t left()	{return _len - _pos;	}
	inline void clear()
	{
		reset();
		_reader.clear(); 
		_writer = nullptr; 
	}
	inline void read(const UInt8 * buf, size_t len)
	{
		_reader.insert(_reader.end(), buf, buf + len);
	}
	inline int write(UInt8 * buf, size_t len)
	{
		if(_pos + len > _len)
			len = _len - _pos;
		if(len == 0)
			return 0;
		memcpy(buf, &_writer[_pos], len);
		_pos += len;
		return (int)len;
	}
	template<typename T>
	inline MemStream& operator<<(const T& v)
	{
		read((UInt8 *)&v, sizeof(T));
		return *this;
	}
	inline MemStream& operator<<(const std::string& v)
	{
		(*this) << static_cast<UInt16>(v.length());
		_reader.insert(_reader.end(), v.begin(), v.end());
		return *this;
	}
	template <typename T>
	inline MemStream& operator<<(T * v)
	{
		read((UInt8 *)&v, sizeof(T *));
		return *this;
	}
	template <typename T>
	inline MemStream& operator<<(const T * v)
	{
		read((UInt8 *)&v, sizeof(T *));
		return *this;
	}
	inline MemStream& operator<<(const char * v)
	{
		if(v == nullptr)
		{
			(*this) << static_cast<UInt16>(0);
			return *this;
		}
		UInt16 len = static_cast<UInt16>(strlen(v));
		(*this) << len;
		_reader.insert(_reader.end(), v, v + len);
		return *this;
	}
	inline MemStream& operator<<(char * v)
	{
		if(v == nullptr)
		{
			(*this) << static_cast<UInt16>(0);
			return *this;
		}
		UInt16 len = static_cast<UInt16>(strlen(v));
		(*this) << len;
		_reader.insert(_reader.end(), v, v + len);
		return *this;
	}
	//
	inline MemStream& operator<<(Stream& st)
	{
		_reader.insert( _reader.end(), &st[0], &st[0]+st.size() );
		return *this;
	}
	inline MemStream& operator>>(Stream& st)
	{
		//_pos, _len
		st.read( const_cast<UInt8 *>(&_writer[_pos]), _len - _pos );
		_pos = _len;
		return *this;
	}
	template<typename T>
	inline MemStream& operator>>(T& v)
	{
		write((UInt8 *)&v, sizeof(T));
		return *this;
	}
	inline MemStream& operator>>(std::string& v)
	{
		UInt16 l;
		(*this) >> l;
		UInt32 lft = static_cast<UInt32>(left());
		if(l > lft)
			l = lft;
		v.resize(l);
		if(l > 0)
		{
			memcpy(&v[0], &_writer[_pos], l);
			_pos += l;
		}
		return *this;
	}
	template<typename T>
	inline MemStream& operator>>(T *& v)
	{
		write((UInt8 *)&v, sizeof(T *));
		return *this;
	}
	template <typename T>
	inline MemStream& operator>>(const T *& v)
	{
		write((UInt8 *)&v, sizeof(T *));
		return *this;
	}
	inline UInt8 * body()
	{
		if ( _reader.empty() )
			return nullptr;
		return &_reader[0];
	}
	template<typename T>
	inline T& data(size_t offset = 0)
	{
		if(offset + sizeof(T) > _reader.size())
		{
			static T tmp(0);
			return tmp;
		}
		return *(T *)&_reader[offset];
	}
	inline UInt8 * writerBody()
	{
		return const_cast<UInt8 *>(_writer);
	}
	inline size_t writeSize()
	{
		return _len;
	}

protected:
	size_t _pos, _len;
	const UInt8 * _writer;
	std::vector<UInt8> _reader;
};

#endif